Титановый отвар
Функциональный Python. head, rest, last и tail
7/04/2020

Думаю, все помнят о бодром способе склейки словарей и распаковки списков:

>>> {'x': 1, **{'y': 2}}
{'x': 1, 'y': 2}
>>> print(*[1, [], 3])
1 [] 3

А я сегодня случайно наткнулся на чудесный PEP-3132. Он повествует о нетрадиционном применении оператора распаковки итераторов *.

head:

>>> xs = [1, 2, 3, 4, 5]
>>> head, *rest = xs
>>> head
1
>>> rest
[2, 3, 4, 5]

last:

>>> xs = [1, 2, 3, 4, 5]
>>> *rest, last = xs
>>> last
5
>>> rest
[1, 2, 3, 4]

Никто не запрещает взять голову и хвост списка одновременно:

>>> xs = [1, 2, 3, 4, 5]
>>> head, *rest, last = xs
>>> head
1
>>> rest
[2, 3, 4]
>>> last
5

Если *rest не нужен -- можно совершенно спокойно заменить его на *_. Хотя мне такое уже не нравится, какой-то perl выходит. :)

И даже вот так можно:

>>> xs = [1, 2, 3, 4, 5, 6]
>>> first, second, *rest, pre_last, last = xs
>>> first
1
>>> second
2
>>> rest
[3, 4]
>>> pre_last
5
>>> last
6

Единственная проблема в том, что нужно быть 100% уверенным в содержимом списка: сколько в нём элементов и какие элементы где возможны.

В случае, когда количество распаковываемых элементов совпадает с длиной распаковываемого списка, в *rest приедет пустой список. Отличить это поведение от штатной обработки списка типа [1, [], 2] невозможно:

>>> xs = [1, 2]
>>> first, *rest, last = xs
>>> first
1
>>> last
2
>>> rest
[]

В случае, когда список короче желаемого, разумеется, будет вылетать исключение:

>>> xs = [1, 2]
>>> a, *b, c, d = xs
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: not enough values to unpack (expected at least 3, got 2)

Такая вот красота. По-моему, такой способ записи в некоторых случаях может выглядеть намного нагляднее стандартного питоньего xs[0] для головы списка, xs[1:] для хвоста и так далее.